/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/tools/Cipher.h"

namespace rgcpp
{

Cipher::Cipher() : random(NULL)
{
	random = fopen("/dev/urandom", "r");
}

Cipher::~Cipher()
{
	if(random) fclose(random);
}

bool Cipher::generateKey(char *key, unsigned int keylen)
{
	if(!random)
		return false;

	if(fread(key, 1, keylen, random) != keylen)
		return false;
	return true;
}

int Cipher::encrypt(char *key, unsigned int keylen, char *in, unsigned int inlen, char *out, unsigned int outlen)
{
	if(outlen<(inlen + EVP_MAX_BLOCK_LENGTH))
	{
		Throw(BufferOverflowException("Output buffer don't have enough size."));
	}

	EVP_CIPHER_CTX ctx;

	EVP_CIPHER_CTX_init(&ctx);
	EVP_CipherInit_ex(&ctx, EVP_aes_128_cbc(), NULL,NULL, NULL,1);
	EVP_CIPHER_CTX_set_key_length(&ctx, keylen);
	EVP_CipherInit_ex(&ctx, NULL, NULL,(unsigned char *)key, NULL,1);

	int length;
	int totalLength;
	if(!EVP_CipherUpdate(&ctx,(unsigned char *)out,&length,(unsigned char *)in, inlen))
	{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	totalLength = length;

	if(!EVP_CipherFinal_ex(&ctx,(unsigned char *)out+totalLength, &length))
	{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	totalLength += length;

	EVP_CIPHER_CTX_cleanup(&ctx);

	return totalLength;
}

int Cipher::decrypt(char *key, unsigned int keylen, char *in, unsigned int inlen, char *out, unsigned int outlen)
{
	if(outlen<(inlen + EVP_MAX_BLOCK_LENGTH))
	{
		Throw(BufferOverflowException("Output buffer don't have enough size."));
	}

	EVP_CIPHER_CTX ctx;

	EVP_CIPHER_CTX_init(&ctx);
	EVP_CipherInit_ex(&ctx, EVP_aes_128_cbc(), NULL,NULL, NULL,0);
	EVP_CIPHER_CTX_set_key_length(&ctx, keylen);
	EVP_CipherInit_ex(&ctx, NULL, NULL,(unsigned char *)key, NULL,0);

	int length;
	int totalLength;
	if(!EVP_CipherUpdate(&ctx,(unsigned char *)out, &length,(unsigned char *)in, inlen))
	{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	totalLength = length;

	if(!EVP_CipherFinal_ex(&ctx,(unsigned char *)out+totalLength, &totalLength))
	{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	totalLength += length;

	EVP_CIPHER_CTX_cleanup(&ctx);

	return totalLength;
}

}
